﻿using System;
using System.Collections.Generic;
using System.Linq;
using MoroMoro.Samples.ToDoList.Base.Domain.Builders;
using MoroMoro.Samples.ToDoList.Domain.Categories;

namespace MoroMoro.Samples.ToDoList.Domain.ToDos
{
    partial class ToDo
    {
        /// <summary>
        /// <see cref="ToDo"/> のビルダです。
        /// </summary>
        [Serializable]
        public sealed class Builder : EntityBuilder<ToDo, IBuildErrorHandler, IConstraintValidator>
        {
            /// <summary>
            /// <see cref="ToDo.Title">ToDo のタイトル</see>の文字数の最大値を取得します。
            /// </summary>
            public static readonly int TitleMaxLength = 100;

            /// <summary>
            /// <see cref="ToDo.Memos">ToDo のメモ一覧</see>の要素数の最大値を取得します。
            /// </summary>
            public static readonly int MemosMaxCount = 5;


            /// <summary>
            /// <see cref="ToDo"/> の作成を目的として、<see cref="Builder"/> クラスの新しいインスタンスを初期化します。
            /// </summary>
            /// <param name="validator">制約検証オブジェクト。</param>
            /// <exception cref="System.ArgumentNullException">validator が null です。</exception>
            public Builder(IConstraintValidator validator)
                : base(validator)
            {
                _memoBuilders = new List<Memo.Builder>();
            }

            /// <summary>
            /// <see cref="ToDo"/> の編集を目的として、<see cref="Builder"/> クラスの新しいインスタンスを初期化します。
            /// </summary>
            /// <param name="target">編集対象の <see cref="ToDo"/>。</param>
            /// <param name="validator">制約検証オブジェクト。</param>
            /// <exception cref="System.ArgumentNullException">target または validator が null です。</exception>
            public Builder(ToDo target, IConstraintValidator validator)
                : base(target, validator)
            {
                _memoBuilders = Target.Memos.Select(memo => new Memo.Builder(memo)).ToList();
                Title = Target.Title;
                Category = Target.Category;
            }


            /// <summary>
            /// <see cref="Memo.Builder">メモビルダ</see>の一覧。
            /// </summary>
            private readonly List<Memo.Builder> _memoBuilders;


            /// <summary>
            /// <see cref="ToDo.Title">ToDo のタイトル</see>に指定する値を取得または設定します。
            /// </summary>
            public string Title
            {
                get;
                set;
            }

            /// <summary>
            /// <see cref="ToDo.Category">ToDo のカテゴリ</see>に指定する値を取得または設定します。
            /// </summary>
            public Category Category
            {
                get;
                set;
            }

            /// <summary>
            /// <see cref="ToDo.Memos">ToDo のメモ一覧</see>を構築する<see cref="Memo.Builder">メモビルダ</see>の一覧を取得します。
            /// </summary>
            public List<Memo.Builder> MemoBuilders
            {
                get
                {
                    return _memoBuilders;
                }
            }


            /// <summary>
            /// <see cref="Memo.Builder">メモビルダ</see>を生成します。
            /// </summary>
            /// <rereturns><see cref="Memo.Builder">メモビルダ</see>。</rereturns>
            public Memo.Builder CreateMemoBuilder()
            {
                return new Memo.Builder();
            }


            /// <summary>
            /// ビルダの現在のエラーを通知します。エラーが無い場合はハンドラには何も通知されません。
            /// </summary>
            /// <param name="handler">エラーハンドラ。</param>
            /// <exception cref="System.ArgumentNullException">handler が null です。</exception>
            public override void NotifyErrorTo(IBuildErrorHandler handler)
            {
                if (handler == null)
                {
                    throw new ArgumentNullException("handler");
                }

                NotifyTitleErrorTo(handler);
                NotifyMemosErrorTo(handler);
            }

            /// <summary>
            /// <see cref="ToDo.Title">ToDo のタイトル</see>に関するエラーを通知します。エラーが無い場合はハンドラには何も通知されません。
            /// </summary>
            /// <param name="handler">エラーハンドラ。</param>
            /// <exception cref="System.ArgumentNullException">handler が null です。</exception>
            public void NotifyTitleErrorTo(ITitleErrorHandler handler)
            {
                if (handler == null)
                {
                    throw new ArgumentNullException("handler");
                }

                if (string.IsNullOrWhiteSpace(Title))
                {
                    handler.HandleTitleEmptyError();
                }
                if ((Title != null) && (TitleMaxLength < Title.Length))
                {
                    handler.HandleTitleTooLongError();
                }
                if (IsForCreation || (!string.Equals(Target.Title, Title, StringComparison.OrdinalIgnoreCase)))
                {
                    if ((Title != null) && (!Validator.TitleIsUnique(Title)))
                    {
                        handler.HandleTitleDuplicationError();
                    }
                }
            }

            /// <summary>
            /// <see cref="ToDo.Memos">ToDo のメモ一覧</see>に関するエラーを通知します。エラーが無い場合はハンドラには何も通知されません。
            /// </summary>
            /// <param name="handler">エラーハンドラ。</param>
            /// <exception cref="System.ArgumentNullException">handler が null です。</exception>
            public void NotifyMemosErrorTo(IMemosErrorHandler handler)
            {
                if (handler == null)
                {
                    throw new ArgumentNullException("handler");
                }

                if (MemosMaxCount < _memoBuilders.Count())
                {
                    handler.HandleMemosTooManyError();
                }
                foreach (var memoBuilder in _memoBuilders)
                {
                    if (!memoBuilder.IsValid)
                    {
                        handler.HandleMemosErrorContainingError();
                        break;
                    }
                }
            }


            /// <summary>
            /// <see cref="ToDo"/> をビルドします。
            /// </summary>
            /// <returns>ビルドされた <see cref="ToDo"/>。</returns>
            /// <exception cref="System.InvalidOperationException"><see cref="ToDo"/> をビルドできる状態ではありません。</exception>
            public override ToDo Build()
            {
                if (!IsValid)
                {
                    throw new InvalidOperationException();
                }

                var result = IsForCreation ? new ToDo() : Target;

                result.Title = Title;

                if (result.Category != Category)
                {
                    if (result.Category != null)
                    {
                        result.Category.ToDos.Remove(result);
                    }

                    result.Category = Category;

                    if (Category != null)
                    {
                        Category.ToDos.Add(result);
                    }
                }

                result.Memos.Clear();
                foreach (var memoBuilder in _memoBuilders)
                {
                    result.Memos.Add(memoBuilder.Build());
                }

                return result;
            }
        }
    }
}
